package org.jackysoft.service.impl;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.Collection;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.Part;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.jackysoft.data.AbstractDataProvider;
import org.jackysoft.entity.ActionNode;
import org.jackysoft.entity.GlobalActionsNode;
import org.jackysoft.entity.GlobalConditionsNode;
import org.jackysoft.entity.InitialActionsNode;
import org.jackysoft.entity.JoinNode;
import org.jackysoft.entity.RegistersNode;
import org.jackysoft.entity.SplitNode;
import org.jackysoft.entity.StepNode;
import org.jackysoft.entity.TriggerFunctionNode;
import org.jackysoft.entity.WorkflowNode;
import org.jackysoft.mapper.WorkflowNodeMapper;
import org.jackysoft.service.OsVariableContextService;
import org.jackysoft.service.OsWorkflowRunnerService;
import org.jackysoft.service.WorkflowNodeService;
import org.jackysoft.util.IgnoreDtdValidateResolver;
import org.jackysoft.util.StringUtils;
import org.jackysoft.workflow.Workflow;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;

import com.google.common.collect.Maps;

@Service
public class WorkflowNodeServiceImpl extends
		AbstractDataProvider<WorkflowNode> implements WorkflowNodeService {

	private static final Log logger = LogFactory.getLog(WorkflowNodeServiceImpl.class);
	
	@Resource
	private OsWorkflowRunnerService workflowService;
	@Resource
	private OsVariableContextService varsetService;

	
	@Resource
	WorkflowNodeMapper mapper;
	
	public static final Map<String,WorkflowNode> cached = Maps.newHashMap();
	
	
	public WorkflowNodeServiceImpl() {
		super();
	}

	

	@Override
	public void remove(String id) throws DataAccessException {
		
		varsetService.removeByRefname(id);	
		mapper.remove(id);
		cached.remove(id);
	}
	

	@Override
	public void upload(Part temp,WorkflowNode bean) {

		String  refname = StringUtils.splitPreFileName(temp.getSubmittedFileName());
		
		WorkflowNode  name = mapper.findOne(refname);		
		
		try {
			if(name==null) name = this.documentOf(temp.getInputStream());
				     
		} catch (IOException e) {			
			logger.error(e);
		}
    	mapper.inert(name);	
    	
    	cached.put(refname, name);

	}

	
	

	@Override
	public Collection<WorkflowNode> getSimples() {
		Collection<WorkflowNode> beans = mapper.getSimples();
		return beans;
	}


	@Override
	public InitialActionsNode getInitialActionsNode(String refname) {

		WorkflowNode name = cached.get(refname);
		if(name==null) name = this.find(refname);
		return name.getInitialActions();		
		
	}

	@Override
	public GlobalActionsNode getGlobalActionsNode(String refname) {
		
		WorkflowNode name = cached.get(refname);
		if(name==null) name = this.find(refname);
		
		return name.getGlobalActions();
		
	}

	@Override
	public GlobalConditionsNode getGlobalConditionsNode(String refname) {

		WorkflowNode name = cached.get(refname);
		if(name==null) name = this.find(refname);
		
		return name.getGlobalConditions();
		
	}

	@Override
	public RegistersNode getRegistersNode(String refname) {
		
		WorkflowNode name = cached.get(refname);
		if(name==null) name = this.find(refname);
		
		return name.getRegisters();
		
		
	}

	protected int getNodeId(String refname,String xpath) {
		WorkflowNode desc = Workflow.WORKFLOW_DESCRIPTOR.get();
		Document doc = desc.document();
		Element node = (Element) doc.selectSingleNode(xpath);
		if (node == null) {
			logger.error(String.format("Node not found xpath:[%s] ", xpath));
			return -1;
		}
		String id = node.attributeValue("id");
		return Integer.valueOf(id);
	}

	protected WorkflowNode documentOf(InputStream input){
		SAXReader sreader = new SAXReader();
		sreader.setEntityResolver(new IgnoreDtdValidateResolver());
		Document doc = null;
		try {
			doc = sreader.read(input);
		} catch (DocumentException e) {
			logger.error(e);
		}
		WorkflowNode wdesc = new WorkflowNode(doc.getRootElement());
		wdesc.setContent(doc.asXML());
		return wdesc;
	}
	
	protected WorkflowNode documentOf(StringReader input){
		SAXReader sreader = new SAXReader();
		sreader.setEntityResolver(new IgnoreDtdValidateResolver());
		Document doc = null;
		try {
			doc = sreader.read(input);
		} catch (DocumentException e) {
			logger.error(e);
		}
		WorkflowNode wdesc = new WorkflowNode(doc.getRootElement());
		wdesc.setContent(doc.asXML());
		return wdesc;
	}



	@Override
	public ActionNode getActionNode(int id, String refname) {
		WorkflowNode name = cached.get(refname);
		if(name==null) name = this.find(refname);
		String xpath = String.format("//action[id=%d]",id);
		Document doc = name.document();
		Element dom = (Element) doc.selectSingleNode(xpath);
		ActionNode node = new ActionNode(dom);
		return node;
	}



	@Override
	public StepNode getStepNode(long id, String refname) {
		WorkflowNode name = cached.get(refname);
		if(name==null) name = this.find(refname);
		String xpath = String.format("//step[id=%d]",id);
		Document doc = name.document();
		Element dom = (Element) doc.selectSingleNode(xpath);
		StepNode node = new StepNode(dom);
	    return node;
	}



	@Override
	public JoinNode getJoinNode(int id, String refname) {
		WorkflowNode name = cached.get(refname);
		if(name==null) name = this.find(refname);
		String xpath = String.format("//join[id=%d]",id);
		Document doc = name.document();
		Element dom = (Element) doc.selectSingleNode(xpath);
		JoinNode node = new JoinNode(dom);
	    return node;
	}



	@Override
	public SplitNode getSplitNode(int id, String refname) {
		WorkflowNode name = cached.get(refname);
		if(name==null) name = this.find(refname);
		String xpath = String.format("//split[id=%d]",id);
		Document doc = name.document();
		Element dom = (Element) doc.selectSingleNode(xpath);
		SplitNode node = new SplitNode(dom);
	    return node;
	}



	@Override
	public String getXml(String refname) {
		String xml = mapper.findDescription(refname);
		return xml;
	}



	@Override
	public TriggerFunctionNode getTriggerFunctionNode(int id,
			String refname) {
		WorkflowNode name = cached.get(refname);
		if(name==null) name = this.find(refname);
		String xpath = String.format("//trigger-function[id=%d]",id);
		Document doc = name.document();
		Element dom = (Element) doc.selectSingleNode(xpath);
		TriggerFunctionNode node = new TriggerFunctionNode(dom);
		return node;
	}
}
